﻿using System;
using System.Collections.Generic;
using System.Linq;
using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

namespace DayWalker.Helpers
{
    public static partial class Helper
    {
        #region AuraCache
        private static LocalPlayer Me { get { return StyxWoW.Me; } }
        //private class AuraCacheClass
        //{
        //    internal DateTime AuraCacheExpireTime { get; set; }
        //    internal ulong AuraCacheUnit { get; set; }
        //    internal WoWAura AuraCacheAura { get; set; }
        //    internal string AuraCacheName { get; set; }
        //    internal int AuraCacheId { get; set; }
        //}

        //private static readonly List<AuraCacheClass> AuraCacheList = new List<AuraCacheClass>();
        //private static TimeSpan AuraCacheExpire;

        //private static void AuraCacheUpdate(WoWUnit target, bool ForceUpdate = false)
        //{
        //    var AuraCacheUnit = AuraCacheList.FirstOrDefault(unit => unit.AuraCacheUnit == target.Guid);

        //    if (ForceUpdate)
        //    {
        //        if (AuraCacheUnit != null)
        //        {
        //            AuraCacheList.RemoveAll(unit => AuraCacheUnit.AuraCacheUnit == target.Guid);
        //        }

        //        var expireTime = DateTime.Now + AuraCacheExpire;
        //        foreach (var aura in target.GetAllAuras())
        //        {
        //            //Logging.Write("Add {0} from {1} ({2}) to AuraCacheUnit", aura.Name, target.Guid, target.SafeName);
        //            AuraCacheList.Add(new AuraCacheClass
        //                {
        //                    AuraCacheExpireTime = expireTime,
        //                    AuraCacheUnit = target.Guid,
        //                    AuraCacheAura = aura,
        //                    AuraCacheName = aura.Name,
        //                    AuraCacheId = aura.SpellId,
        //                });
        //        }
        //    }


        //    if (AuraCacheUnit == null)
        //    {
        //        //Logging.Write("AuraCacheList Do Not Containt {0} Add All Aura to AuraCacheList", target.SafeName);
        //        var expireTime = DateTime.Now + AuraCacheExpire;
        //        foreach (var aura in target.GetAllAuras())
        //        {
        //            //Logging.Write("Add {0} from {1} ({2}) to AuraCacheUnit", aura.Name, target.Guid, target.SafeName);
        //            AuraCacheList.Add(new AuraCacheClass
        //                {
        //                    AuraCacheExpireTime = expireTime,
        //                    AuraCacheUnit = target.Guid,
        //                    AuraCacheAura = aura,
        //                    AuraCacheName = aura.Name,
        //                    AuraCacheId = aura.SpellId,
        //                });
        //        }

        //        //Logging.Write("Total AuraCacheList for {0}", target.SafeName);
        //        //foreach (var aura in AuraCacheList)
        //        //{
        //        //    if (aura.AuraCacheUnit != target)
        //        //    {
        //        //        continue;
        //        //    }
        //        //    Logging.Write("{0} - {1}", aura.AuraCacheId, aura.AuraCacheAura.Name);
        //        //}
        //    }
        //    else if (AuraCacheUnit.AuraCacheExpireTime < DateTime.Now)
        //    {
        //        //Logging.Write("AuraCacheList Containt {0} But Cache Time Expired, Update All Aura to AuraCacheList",
        //        //              target.SafeName);

        //        AuraCacheList.RemoveAll(unit => AuraCacheUnit.AuraCacheUnit == target.Guid);

        //        var expireTime = DateTime.Now + AuraCacheExpire;
        //        foreach (var aura in target.GetAllAuras())
        //        {
        //            //Logging.Write("Add {0} from {1} ({2}) to AuraCacheUnit", aura.Name, target.Guid, target.SafeName);
        //            AuraCacheList.Add(new AuraCacheClass
        //                {
        //                    AuraCacheExpireTime = expireTime,
        //                    AuraCacheUnit = target.Guid,
        //                    AuraCacheAura = aura,
        //                    AuraCacheName = aura.Name,
        //                    AuraCacheId = aura.SpellId,
        //                });
        //        }

        //        //Logging.Write("Total AuraCacheList for {0}", target.SafeName);
        //        //foreach (var aura in AuraCacheList)
        //        //{
        //        //    if (aura.AuraCacheUnit != target)
        //        //    {
        //        //        continue;
        //        //    }
        //        //    Logging.Write("{0} - {1}", aura.AuraCacheId, aura.AuraCacheAura.Name);
        //        //}
        //    }
        //    //else
        //    //{
        //    //    Logging.Write("AuraCacheList Containt {0} and Cache Time NOT Expired, Do Not update to AuraCacheList",
        //    //                  target.SafeName);

        //    //    Logging.Write("Total AuraCacheList for {0}", target.SafeName);
        //    //    foreach (var aura in AuraCacheList)
        //    //    {
        //    //        if (aura.AuraCacheUnit != target)
        //    //        {
        //    //            continue;
        //    //        }
        //    //        Logging.Write("{0} - {1}", aura.AuraCacheId, aura.AuraCacheAura.Name);
        //    //    }
        //    //}
        //}

        #endregion

        #region SpellsCooldownHS

        public static readonly HashSet<string> SpellsCooldownHS = new HashSet<string>
            {
                //Racial
                "Arcane Torrent",
                "Berserking",
                "Blood Fury",
                "Cannibalize",
                "Darkflight",
                "Escape Artist",
                "Every Man for Himself",
                "Gift of the Naaru",
                "Rocket Jump",
                "Shadowmeld",
                "Stoneform",
                "War Stomp",
                "Will of the Forsaken",

                //Class Spells
                "A Murder of Crows",
                "Barrage",
                "Bestial Wrath",
                "Binding Shot",
                "Black Arrow",
                "Camouflage",
                "Chimera Shot",
                "Concussive Shot",
                "Deterrence",
                "Dire Beast",
                "Disengage",
                "Distracting Shot",
                "Exhilaration",
                "Explosive Trap",
                "Feign Death",
                "Fervor",
                "Flare",
                "Freezing Trap",
                "Glaive Toss",
                "Ice Trap",
                "Intimidation",
                "Kill Command",
                "Lynx Rush",
                "Master's Call",
                "Misdirection",
                "Powershot",
                "Rapid Fire",
                "Readiness",
                "Scatter Shot",
                "Silencing Shot",
                "Snake Trap",
                "Stampede",
                "Stampede",
            };

        #endregion

        #region EnemyListCache

        private static DateTime EnemyListCacheClearLast;
        public static readonly Dictionary<ulong, DateTime> EnemyListCache = new Dictionary<ulong, DateTime>();

       public static void EnemyListCacheClear()
        {
            if (EnemyListCacheClearLast > DateTime.Now)
            {
                return;
            }

            EnemyListCacheClearLast = DateTime.Now + TimeSpan.FromSeconds(5);

            var indexToRemove = EnemyListCache.Where(
                unit =>
                unit.Value < DateTime.Now).Select(unit => unit.Key).ToList();

            foreach (var index in indexToRemove)
            {
                //Logging.Write("Remove {0} from EnemyList", EnemyList[index]);
                EnemyListCache.Remove(index);
            }
        }

        public static void EnemyListCacheAdd(WoWUnit unit, int expireSeconds = 10)
        {
            if (MeHasAura("Arena Preparation") || MeHasAura("Preparation"))
            {
                return;
            }
            if (EnemyListCache.ContainsKey(unit.Guid))
            {
                return;
            }
            //Logging.Write("Add {0} ({1}) to EnemyListCache", unit.Guid, unit.Name);
            EnemyListCache.Add(unit.Guid, DateTime.Now + TimeSpan.FromSeconds(expireSeconds));
        }

        #endregion

        #region FriendListCache

        private static DateTime FriendListCacheClearLast;
       public static readonly Dictionary<ulong, DateTime> FriendListCache = new Dictionary<ulong, DateTime>();

       public static void FriendListCacheClear()
        {
            if (FriendListCacheClearLast > DateTime.Now)
            {
                return;
            }

            FriendListCacheClearLast = DateTime.Now + TimeSpan.FromSeconds(5);

            var indexToRemove = FriendListCache.Where(
                unit =>
                unit.Value < DateTime.Now).Select(unit => unit.Key).ToList();

            foreach (var index in indexToRemove)
            {
                //Logging.Write("Remove {0} from FriendList", FriendList[index]);
                FriendListCache.Remove(index);
            }
        }

        public static void FriendListCacheAdd(WoWUnit unit, int expireSeconds = 60)
        {
            if (FriendListCache.ContainsKey(unit.Guid)) return;
            //Logging.Write("Add {0} ({1}) to FriendListCache", unit.Guid, unit.Name);
            FriendListCache.Add(unit.Guid, DateTime.Now + TimeSpan.FromSeconds(expireSeconds));
        }

        #endregion

        #region InLineOfSpellSightCache

        private static readonly Dictionary<ulong, DateTime> InLineOfSpellSightCache = new Dictionary<ulong, DateTime>();

        private static void InLineOfSpellSightCacheClear()
        {
            var indexToRemove = InLineOfSpellSightCache.Where(
                unit =>
                unit.Value < DateTime.Now).Select(unit => unit.Key).ToList();


            foreach (var index in indexToRemove)
            {
                InLineOfSpellSightCache.Remove(index);
            }
        }

        private static void InLineOfSpellSightCacheAdd(WoWUnit unit, int expireMilliseconds = 100)
        {
            if (InLineOfSpellSightCache.ContainsKey(unit.Guid)) return;
            //Logging.Write("Add {0} ({1}) to InLineOfSpellSightCache", unit.Guid, unit.Name);
            InLineOfSpellSightCache.Add(unit.Guid, DateTime.Now + TimeSpan.FromMilliseconds(expireMilliseconds));
        }

        private static bool InLineOfSpellSightCheck(WoWUnit unit)
        {
            if (!BasicCheck(unit))
            {
                return false;
            }

            InLineOfSpellSightCacheClear();

            if (unit == Me || InLineOfSpellSightCache.ContainsKey(unit.Guid))
            {
                return true;
            }

            if (unit.InLineOfSpellSight)
            {
                if (Me.CurrentMap.IsRaid || Me.CurrentMap.IsDungeon)
                {
                    InLineOfSpellSightCacheAdd(unit, 300);
                    return true;
                }
                InLineOfSpellSightCacheAdd(unit);
                return true;
            }
            return false;
        }

        #endregion

        #region CanCastCache

        private static readonly Dictionary<string, DateTime> SpellsCooldownCache = new Dictionary<string, DateTime>();

        private static void SpellsCooldownCacheAdd(string spellName, DateTime spellCooldownBack)
        {
            if (spellName == "Ice Trap" || spellName == "Freezing Trap")
            {
                SpellsCooldownCache.Add("Ice Trap", spellCooldownBack);
                SpellsCooldownCache.Add("Freezing Trap", spellCooldownBack);
            }
            else
            {
                SpellsCooldownCache.Add(spellName, spellCooldownBack);
            }
        }

        private static void SpellsCooldownCacheClear()
        {
            if (MeHasAura("Arena Preparation"))
            {
                SpellsCooldownCache.Clear();
                return;
            }

            var indexToRemove = SpellsCooldownCache.Where(
                unit =>
                unit.Value < DateTime.Now).Select(unit => unit.Key).ToList();


            foreach (var index in indexToRemove)
            {
                try
                {
                    SpellsCooldownCache.Remove(index);
                }
                catch (Exception)
                {
                    Logging.Write("Fail to SpellsCooldownCache.Remove({0})", index);
                    throw;
                }
            }
        }

        private static double PowerCost;
        private static double CastTime;
        private static DateTime spellCooldownBack;

        private static bool CanCastCheckOld(string spellName, bool IsOffGCDSpell = false)
        {
            SpellsCooldownCacheClear();

            if (UseSpecialization == 3 &&
                SpellsCooldownCache.ContainsKey("Explosive Shot") &&
                MeHasAura("Lock and Load"))
            {
                var indexToRemove = SpellsCooldownCache.Where(
                    unit =>
                    unit.Key == "Explosive Shot").Select(unit => unit.Key).ToList();


                foreach (var index in indexToRemove)
                {
                    try
                    {
                        SpellsCooldownCache.Remove(index);
                    }
                    catch (Exception)
                    {
                        Logging.Write("Fail to SpellsCooldownCache.Remove({0})", index);
                        throw;
                    }
                }
            }

            if (SpellsCooldownCache.ContainsKey(spellName))
            {
                //Logging.Write("SpellsCooldownCache contains {0}, skip check", spellName);
                return false;
            }

            if (!SpellManager.HasSpell(spellName))
            {
                return false;
            }

            if ((Me.CurrentMap.IsArena || Me.CurrentMap.IsBattleground) &&
                spellName != "Bestial Wrath" &&
                spellName != "Every Man for Himself" &&
                DebuffCC(Me))
            {
                return false;
            }

            if (spellName == "Aimed Shot" &&
                MeHasAura(82926))
            {
                PowerCost = 0;
            }
            else if (spellName == "Explosive Shot" &&
                     MeHasAura("Lock and Load"))
            {
                PowerCost = 0;
            }
            else if ((spellName == "Arcane Shot" ||
                      spellName == "Multi-Shots") &&
                     MeHasAura("Thrill of the Hunt"))
            {
                PowerCost = GetSpellPowerCost(spellName) - 20;
            }
            else
            {
                PowerCost = GetSpellPowerCost(spellName);
            }

            //if (PlayerFocus() < PowerCost)
            //{
              //  return false;
            //}

            if (!IsMoving(Me) ||
                spellName == "Aimed Shot" && MeHasAura(82926) ||
                spellName == "Aimed Shot" && HasGlyph.Contains("126095") ||
                spellName == "Cobra Shot" ||
                spellName == "Steady Shot")
            {
                CastTime = 0;
            }
            else
            {
                CastTime = GetSpellCastTime(spellName);
            }

            if (CastTime > 0)
            {
                return false;
            }

            if (IsOffGCDSpell || !Me.IsCasting || Me.IsCasting &&
                Me.CurrentCastTimeLeft.TotalMilliseconds <= MyLatency &&
                Me.CurrentChannelTimeLeft.TotalMilliseconds <= MyLatency)
            {
                if (IsOffGCDSpell)
                {
                    spellCooldownBack = DateTime.Now + GetSpellCooldown(spellName) -
                                        TimeSpan.FromMilliseconds(1500);
                }
                else if (spellName == "Chimera Shot" ||
                         spellName == "Kill Command" ||
                         spellName == "Barrage" ||
                         spellName == "Kill Shot" ||
                         spellName == "Explosive Shot")
                {
                    spellCooldownBack = DateTime.Now + GetSpellCooldown(spellName) -
                                        TimeSpan.FromMilliseconds(MyLatency + 300);
                }
                else
                {
                    spellCooldownBack = DateTime.Now + GetSpellCooldown(spellName) -
                                        TimeSpan.FromMilliseconds(MyLatency);
                }

                if (spellCooldownBack <= DateTime.Now)
                {
                    return true;
                }
            }

            //if (spellName == "Aimed Shot" &&
            //    (MeHasAura(82926) || HasGlyph.Contains("126095") && PlayerFocus() >= 50) &&
            //    (!Me.IsCasting && SpellManager.GlobalCooldownLeft.TotalMilliseconds <= MyLatency ||
            //     Me.IsCasting && Me.CurrentCastTimeLeft.TotalMilliseconds <= MyLatency &&
            //     Me.CurrentChannelTimeLeft.TotalMilliseconds <= MyLatency) ||
            //    spellName == "Cobra Shot" &&
            //    (!Me.IsCasting && SpellManager.GlobalCooldownLeft.TotalMilliseconds <= MyLatency ||
            //     Me.IsCasting && Me.CurrentCastTimeLeft.TotalMilliseconds <= MyLatency &&
            //     Me.CurrentChannelTimeLeft.TotalMilliseconds <= MyLatency) ||
            //    spellName == "Steady Shot" &&
            //    (!Me.IsCasting && SpellManager.GlobalCooldownLeft.TotalMilliseconds <= MyLatency ||
            //     Me.IsCasting && Me.CurrentCastTimeLeft.TotalMilliseconds <= MyLatency &&
            //     Me.CurrentChannelTimeLeft.TotalMilliseconds <= MyLatency) ||
            //    SpellManager.CanCast(spellName))
            //{
            //    return true;
            //}

            //if (IsOffGCDSpell &&
            //    GetSpellPowerCost(spellName) < PlayerFocus() &&
            //    GetSpellCooldown(spellName).TotalMilliseconds < 1500)
            //{
            //    return true;
            //}

            //if (spellName == "Chimera Shot" ||
            //    spellName == "Kill Command" ||
            //    spellName == "Barrage" ||
            //    spellName == "Kill Shot" ||
            //    spellName == "Explosive Shot")
            //{
            //    spellCooldownBack = DateTime.Now + GetSpellCooldown(spellName) -
            //                        TimeSpan.FromMilliseconds(MyLatency + 300);
            //}
            //else
            //{
            //    spellCooldownBack = DateTime.Now + GetSpellCooldown(spellName) -
            //                        TimeSpan.FromMilliseconds(MyLatency);
            //}

            SpellsCooldownCacheAdd(spellName, spellCooldownBack);

            return false;
        }

        private static readonly HashSet<string> OverrideToSpellHS = new HashSet<string>
            {
                "Aspect of the Iron Hawk", //109260
                "Silencing Shot", //34490
                "Explosive Trap", //82939
                "Freezing Trap", //60192
                "Snake Trap", //82948
                "Cobra Shot", //77767
            };

        private static int OverrideToSpellId(string spellName)
        {
            switch (spellName)
            {
                case "Aspect of the Iron Hawk":
                    return 109260;
                case "Silencing Shot":
                    return 34490;
                case "Explosive Trap":
                    return 82939;
                case "Freezing Trap":
                    return 60192;
                case "Snake Trap":
                    return 82948;
                case "Cobra Shot":
                    return 77767;
            }
            return 0;
        }

        private static bool CanCastCheck(string spellName)
        {
            if (!SpellManager.HasSpell(spellName))
            {
                return false;
            }

            if ((Me.CurrentMap.IsArena || Me.CurrentMap.IsBattleground) &&
                spellName != "Bestial Wrath" &&
                spellName != "Every Man for Himself" &&
                DebuffCC(Me))
            {
                return false;
            }

            WoWSpell actualSpel;
            if (OverrideToSpellHS.Contains(spellName) && SpellManager.HasSpell(OverrideToSpellId(spellName)))
            {
                actualSpel = WoWSpell.FromId(OverrideToSpellId(spellName));
            }
            else
            {
                actualSpel = SpellManager.Spells[spellName];
            }


            if (actualSpel.CooldownTimeLeft.TotalMilliseconds <= 0 &&
                (actualSpel.PowerCost <= 0 ||
                 actualSpel.PowerCost < Me.CurrentFocus))
            {
                return true;
            }

            if (spellName == "Kill Shot" &&
                actualSpel.CooldownTimeLeft.TotalMilliseconds < 1000)
            {
                return true;
            }

            if ((spellName == "Steady Shot" ||
                 spellName == "Cobra Shot") &&
                IsMoving(Me) &&
                //(!Me.IsCasting ||
                // Me.IsCasting &&
                // Me.CurrentCastTimeLeft.TotalMilliseconds <= MyLatency &&
                // Me.CurrentChannelTimeLeft.TotalMilliseconds <= MyLatency) &&
                SpellManager.HasSpell("Mend Pet") &&
                SpellManager.CanCast("Mend Pet"))
            {
                return true;
            }

            if (spellName == "Aimed Shot" &&
                IsMoving(Me) &&
                MeHasAura(82926) &&
                SpellManager.HasSpell("Mend Pet") &&
                SpellManager.CanCast("Mend Pet"))
            {
                return true;
            }

            if (spellName == "Aimed Shot" &&
                IsMoving(Me) &&
                HasGlyph.Contains("126095") &&
                //(!Me.IsCasting ||
                // Me.IsCasting &&
                // Me.CurrentCastTimeLeft.TotalMilliseconds <= MyLatency &&
                // Me.CurrentChannelTimeLeft.TotalMilliseconds <= MyLatency) &&
                SpellManager.HasSpell("Mend Pet") &&
                SpellManager.CanCast("Mend Pet") &&
                Me.CurrentFocus >= 50)
            {
                return true;
            }

            if (SpellManager.CanCast(actualSpel))
            {
                return true;
            }

            return false;
        }

        #endregion

        #region CanUseCache

        private static readonly Dictionary<WoWItem, DateTime> ItemsCooldownCache = new Dictionary<WoWItem, DateTime>();
        private static TimeSpan ItemCooldownBack;

        private static TimeSpan GetItemCooldown(WoWItem Item)
        {
            if (Item != null && Item.Usable)
            {
                return Item.CooldownTimeLeft;
            }
            return TimeSpan.FromMinutes(1);
        }

        private static void ItemsCooldownCacheAdd(WoWItem Item, DateTime CooldownBack)
        {
            //Logging.Write("Add {0} to ItemsCooldownCache, cooldown is back in {1} ms.", Item, ItemCooldownBack);
            ItemsCooldownCache.Add(Item, CooldownBack);
        }

        private static void ItemsCooldownCacheClear()
        {
            var indexToRemove = ItemsCooldownCache.Where(
                unit =>
                unit.Value <= DateTime.Now).Select(unit => unit.Key).ToList();


            foreach (var index in indexToRemove)
            {
                ItemsCooldownCache.Remove(index);
            }
        }

        private static bool CanUseCheck(WoWItem Item)
        {
            ItemsCooldownCacheClear();


            if (ItemsCooldownCache.ContainsKey(Item))
            {
                //Logging.Write("ItemsCooldownCache contains {0}, skip check", Item);
                return false;
            }

            ItemCooldownBack = GetItemCooldown(Item);

            if (ItemCooldownBack.TotalMilliseconds <= 0)
            {
                return true;
            }

            ItemsCooldownCacheAdd(Item, DateTime.Now + ItemCooldownBack);
            return false;
        }

        #endregion
    }
}